/*
 * $Id: stdcli.c,v 1.10 2005-04-24 07:48:16 bacon Exp $
 */

#include <xp/bas/stdcli.h>
#include <xp/bas/string.h>
#include <xp/bas/memory.h>
#include <xp/bas/assert.h>

/* 
 * PARAM_BUNCH should be always 2^n if you don't want to the % operator
 */
#define PARAM_BUNCH 8

static const xp_char_t* __optsta[] =
{
	XP_TEXT("--"),
	XP_NULL
};

static const xp_char_t* __optasn = XP_TEXT("=");
static xp_char_t __emptyval[] = { XP_CHAR('\0') };

static xp_char_t* __begins_with (
	const xp_char_t* str, const xp_char_t* prefixes[])
{
	const xp_char_t** pp = prefixes;

	while (*pp != XP_NULL) {
		const xp_char_t* p1 = str;
		const xp_char_t* p2 = *pp;
		while (*p2 != XP_CHAR('\0')) {
			if (*p1 != *p2) break;
			p1++; p2++;
		}
		if (*p2 == XP_CHAR('\0')) return (xp_char_t*)*pp;

		pp++;
	}

	return XP_NULL;
}

int xp_parsecli (int argc, xp_char_t* const argv[], xp_cli_t* cli)
{
	int index = 1;
	xp_cliopt_t* opt;
	const xp_char_t** optsta = cli->optsta;
	const xp_char_t* optasn = cli->optasn;

	if (optsta == XP_NULL) optsta = __optsta;
	if (optasn == XP_NULL) optasn = __optasn;

	cli->verb = argv[0];
	cli->nparams = 0;
	cli->params = XP_NULL;

	/* initialize value for each opt */
	for (opt = cli->opts; opt->name != XP_NULL; opt++) {
		opt->value = XP_NULL;
	}

	while (index < argc) {
		const xp_char_t* ip;
		xp_char_t* ap = argv[index++];
		xp_char_t* value;

		ip = __begins_with (ap, optsta);
		if (ip != XP_NULL) { /* opt */
			xp_char_t* name;
			xp_size_t namelen;

			ap = xp_strtok (ap + xp_strlen(ip), optasn, &name, &namelen);
			if (namelen == 0) continue;

			for (opt = cli->opts; opt->name != XP_NULL; opt++) {
				if (xp_strxcmp(name, namelen, opt->name) == 0) break;
			}

			value = XP_NULL;
			if (ap == XP_NULL) {
				if (index < argc && 
				    __begins_with(argv[index], optsta) == XP_NULL) {
					value = argv[index++];
				}
			}
			else {
				/* beware that this changes the original text */
				name[namelen] = XP_CHAR('\0'); 
				value = ap;
			}

			if (opt->name == XP_NULL) {
				if (cli->errhandler (cli,
					XP_CLI_ERROR_INVALID_OPTNAME, name, value) == -1) {
					xp_clearcli (cli);
					return -1;
				}
			}
			else {
				if (value != XP_NULL && !(opt->requires & XP_CLI_OPTVAL)) {
					if (cli->errhandler (cli,
						XP_CLI_ERROR_REDUNDANT_OPTVAL, name, value) == -1) {
						xp_clearcli (cli);
						return -1;	
					}
				}
				else if (value == XP_NULL && (opt->requires & XP_CLI_OPTVAL)) {
					if (cli->errhandler (cli,
						XP_CLI_ERROR_MISSING_OPTVAL, name, value) == -1) {
						xp_clearcli (cli);
						return -1;	
					}	
				}
				else if (value == XP_NULL) 
					opt->value = __emptyval;
				else opt->value = value;
			}
		}
		else {
			if ((cli->nparams & (PARAM_BUNCH - 1)) == 0) {
				xp_char_t** t;

				t = (xp_char_t**) xp_realloc (cli->params,
					(cli->nparams + PARAM_BUNCH) * xp_sizeof(xp_char_t*));
				if (t == XP_NULL) {
					if (cli->errhandler (cli,
						XP_CLI_ERROR_MEMORY, ap, XP_NULL) == -1) {
						xp_clearcli (cli);
						return -1;
					}
					else continue;
				}
				cli->params = t;
			}

			cli->params[cli->nparams++] = ap;
		}
	}

	for (opt = cli->opts; opt->name != XP_NULL; opt++) {
		if ((opt->requires & XP_CLI_OPTNAME) && opt->value == XP_NULL) {
			if (cli->errhandler (cli,
				XP_CLI_ERROR_MISSING_OPTNAME, opt->name, XP_NULL) == -1) {
				xp_clearcli (cli);
				return -1;	
			}	
		}
	}

	return 0;
}

void xp_clearcli (xp_cli_t* cli)
{
	if (cli->params != XP_NULL) {
		xp_assert (cli->nparams > 0);
		xp_free (cli->params);
		cli->nparams = 0;
		cli->params = XP_NULL;
	}
}
 
xp_char_t* xp_getcliverb (const xp_cli_t* cli)
{
	return cli->verb;
}

xp_char_t* xp_getclioptval (const xp_cli_t* cli, const xp_char_t* opt)
{
	xp_cliopt_t* q = cli->opts;

	while (q->name != XP_NULL) {
		if (q->value != XP_NULL &&
		    xp_strcmp (opt, q->name) == 0) return q->value;
		q++;
	}

	return XP_NULL;
}

int xp_getncliparams (const xp_cli_t* cli)
{
	return cli->nparams;
}

xp_char_t* xp_getcliparam (const xp_cli_t* cli, int index)
{
	xp_assert (index < cli->nparams);
	return cli->params[index];
}

int xp_getopt (int argc, xp_char_t* const argv[],
	const xp_char_t* optstr, int* optidx, int long_only)
{
	/*
	if (optstr[0] == XP_CHAR('-')) {
		ordering = RETURN_IN_ORDER;
		++optstr;
	}
	else if (optstr[0] == XP_CHAR('+')) {
		ordering = REQUIRE_ORDER;
		++optstr;
	}
	else ordering = PERMUTE;
	*/

	return -1;
}
